﻿/* 
 * Copyright (c) Intel Corporation
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * -- Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * -- Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * -- Neither the name of the Intel Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE INTEL OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Xml;
using System.Text;
using System.Web;
using ExtensionLoader;
using HttpServer;
using OpenMetaverse;
using OpenMetaverse.Http;
using OpenMetaverse.StructuredData;
using CableBeachMessages;

namespace InventoryServer.Extensions
{
    public class FilesystemService : IExtension<InventoryServer>
    {
        InventoryServer server;

        public FilesystemService()
        {
        }

        public bool Start(InventoryServer server)
        {
            this.server = server;

            server.ServiceRegistrationProvider.RegisterService(new Uri(CableBeachServices.FILESYSTEM), CreateCapabilitiesHandler);

            // Filesystem handlers. Usually capabilities are used to access these, but this allows trusted access after doing a client certificate check
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/create_filesystem", TrustedCreateFilesystemHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/create_object", TrustedCreateObjectHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/get_object", TrustedGetObjectHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/get_filesystem_skeleton", TrustedGetFilesystemSkeletonHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/get_root_folder", TrustedGetRootFolderHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/purge_folder", TrustedPurgeFolderHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/delete_object", TrustedDeleteObjectHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/get_folder_contents", TrustedGetFolderContentsHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/get_folder_for_type", TrustedGetFolderForTypeHandler);
            server.HttpServer.AddHandler("POST", null, @"^/filesystem/get_active_gestures", TrustedGetActiveGesturesHandler);

            return true;
        }

        public void Stop()
        {
        }

        void CreateCapabilitiesHandler(Uri identity, ref Dictionary<Uri, Uri> capabilities)
        {
            Uri[] caps = new Uri[capabilities.Count];
            capabilities.Keys.CopyTo(caps, 0);
            bool capReturned = false;

            for (int i = 0; i < caps.Length; i++)
            {
                Uri cap = caps[i];
                string capName = cap.ToString();

                switch (capName)
                {
                    case CableBeachServices.FILESYSTEM_CREATE_FILESYSTEM:
                        capabilities[cap] = server.CreateCapability(CreateFilesystemHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_CREATE_OBJECT:
                        capabilities[cap] = server.CreateCapability(CreateObjectHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_OBJECT:
                        capabilities[cap] = server.CreateCapability(GetObjectHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_FILESYSTEM_SKELETON:
                        capabilities[cap] = server.CreateCapability(GetFilesystemSkeletonHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_FILESYSTEM:
                        capabilities[cap] = server.CreateCapability(GetFilesystemHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_ROOT_FOLDER:
                        capabilities[cap] = server.CreateCapability(GetRootFolderHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_PURGE_FOLDER:
                        capabilities[cap] = server.CreateCapability(PurgeFolderHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_DELETE_OBJECT:
                        capabilities[cap] = server.CreateCapability(DeleteObjectHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_FOLDER_CONTENTS:
                        capabilities[cap] = server.CreateCapability(GetFolderContentsHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_FOLDER_FOR_TYPE:
                        capabilities[cap] = server.CreateCapability(GetFolderForTypeHandler, false, identity);
                        capReturned = true;
                        break;
                    case CableBeachServices.FILESYSTEM_GET_ACTIVE_GESTURES:
                        capabilities[cap] = server.CreateCapability(GetActiveGesturesHandler, false, identity);
                        capReturned = true;
                        break;
                    default:
                        break;
                }
            }

            if (capReturned)
            {
                UUID agentID = server.FilesystemProvider.IdentityToUUID(identity);

                // Create a new skeleton inventory for this identity if one does not already exist
                if (server.FilesystemProvider.GetDefaultParent(identity, agentID, "application/vnd.ll.folder") == UUID.Zero)
                {
                    if (LindenInventoryHelper.CreateDefaultInventory(server, identity, agentID))
                        Logger.Info("[FilesystemService] Created a default (Linden Lab style) inventory for " + identity);
                    else
                        Logger.Error("[FilesystemService] Failed to create a default inventory for " + identity);
                }
            }
        }

        #region Trusted Handlers

        void TrustedCreateFilesystemHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            CreateFilesystemHandler(context, request, response, null);
        }

        void TrustedCreateObjectHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            CreateObjectHandler(context, request, response, null);
        }

        void TrustedPurgeFolderHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            PurgeFolderHandler(context, request, response, null);
        }

        void TrustedDeleteObjectHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            DeleteObjectHandler(context, request, response, null);
        }

        void TrustedGetFolderContentsHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            GetFolderContentsHandler(context, request, response, null);
        }

        void TrustedGetFolderForTypeHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            GetFolderForTypeHandler(context, request, response, null);
        }

        void TrustedGetObjectHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            GetObjectHandler(context, request, response, null);
        }

        void TrustedGetFilesystemSkeletonHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            GetFilesystemSkeletonHandler(context, request, response, null);
        }

        void TrustedGetRootFolderHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            GetRootFolderHandler(context, request, response, null);
        }

        void TrustedGetActiveGesturesHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response)
        {
            // FIXME: Implement client certificate checking
            GetActiveGesturesHandler(context, request, response, null);
        }

        #endregion Trusted Handlers

        #region Filesystem Handlers

        void CreateFilesystemHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                CreateInventoryMessage message = new CreateInventoryMessage();
                message.Deserialize(map);

                CreateInventoryReplyMessage reply = new CreateInventoryReplyMessage();
                reply.Success = false;
                reply.Message = String.Empty;

                Uri identity = message.Identity;
                UUID ownerID = CableBeachUtils.MessageToUUID(message.Identity, message.AgentID);

                // Create a new skeleton inventory for this identity if one does not already exist
                if (server.FilesystemProvider.GetDefaultParent(identity, message.AgentID, "application/vnd.ll.folder") == UUID.Zero)
                {
                    if (LindenInventoryHelper.CreateDefaultInventory(server, identity, message.AgentID))
                    {
                        reply.Success = true;
                        Logger.Info("[FilesystemService] Created a default (Linden Lab style) inventory for " + identity);
                    }
                    else
                    {
                        reply.Message = "failed to create default inventory";
                        Logger.Error("[FilesystemService] Failed to create a default inventory for " + identity);
                    }
                }

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void CreateObjectHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                CreateObjectMessage message = new CreateObjectMessage();
                message.Deserialize(map);

                CreateObjectReplyMessage reply = new CreateObjectReplyMessage();

                if (message.Object is InventoryBlockItem)
                {
                    #region InventoryItem AddOrUpdate

                    InventoryBlockItem incomingObj = (InventoryBlockItem)message.Object;

                    // Try to fetch the existing item first
                    InventoryItem item;
                    if (incomingObj.ID != UUID.Zero &&
                        server.FilesystemProvider.TryFetchItem(message.Identity, message.AgentID, incomingObj.ID, out item) == BackendResponse.Success)
                    {
                        // Item already existed in the filesystem, this is an update

                        // Set the assetID
                        if (incomingObj.AssetID != UUID.Zero)
                            item.AssetID = incomingObj.AssetID;

                        // Set the parentID
                        if (incomingObj.ParentID != UUID.Zero)
                            item.ParentID = incomingObj.ParentID;
                    }
                    else
                    {
                        // No item previously existed, this is a create
                        item = new InventoryItem();

                        // Set the itemID
                        item.ID = (incomingObj.ID != UUID.Zero) ?
                            incomingObj.ID :
                            UUID.Random();

                        // Set the assetID
                        item.AssetID = (incomingObj.AssetID != UUID.Zero) ?
                            incomingObj.AssetID :
                            server.FilesystemProvider.GetDefaultAsset(message.Identity, message.AgentID, incomingObj.ContentType);

                        // Set the parentID
                        item.ParentID = (incomingObj.ParentID != UUID.Zero) ?
                            incomingObj.ParentID :
                            server.FilesystemProvider.GetDefaultParent(message.Identity, message.AgentID, incomingObj.ContentType);

                        item.AssetType = (int)CableBeachUtils.ContentTypeToSLAssetType(incomingObj.ContentType);
                        item.InvType = (int)CableBeachUtils.ContentTypeToSLInvType(incomingObj.ContentType);
                        item.CreationDate = (int)OpenMetaverse.Utils.DateTimeToUnixTime(DateTime.Now);
                    }

                    // Set the name
                    if (!String.IsNullOrEmpty(incomingObj.Name))
                        item.Name = incomingObj.Name;

                    // Set the description
                    if (!String.IsNullOrEmpty(incomingObj.Description))
                        item.Description = incomingObj.Description;

                    // Set everything else
                    item.BasePermissions = incomingObj.PermsBase;
                    item.CurrentPermissions = incomingObj.PermsOwner;
                    item.EveryOnePermissions = incomingObj.PermsEveryone;
                    item.Flags = incomingObj.Flags;
                    item.GroupID = incomingObj.GroupID;
                    item.GroupOwned = incomingObj.GroupOwned;
                    item.GroupPermissions = incomingObj.PermsGroup;
                    item.NextPermissions = incomingObj.PermsNext;
                    item.SalePrice = incomingObj.SalePrice;
                    item.SaleType = (byte)incomingObj.SaleType;

                    if (server.FilesystemProvider.TryCreateItem(message.Identity, message.AgentID, item) == BackendResponse.Success)
                    {
                        reply.Success = true;
                        reply.Object = InventoryToMessage(item);
                    }
                    else
                    {
                        Logger.Warn("[FilesystemService] Failed to create item " + item.Name + " for " + message.Identity);
                        reply.Success = false;
                        reply.Message = "item creation failed";
                    }

                    #endregion InventoryItem AddOrUpdate
                }
                else
                {
                    #region InventoryFolder AddOrUpdate

                    // Create Folder
                    InventoryBlockFolder incomingObj = (InventoryBlockFolder)message.Object;

                    // Try to fetch the existing folder first
                    InventoryFolder folder;
                    if (incomingObj.ID != UUID.Zero &&
                        server.FilesystemProvider.TryFetchFolder(message.Identity, message.AgentID, incomingObj.ID, out folder) == BackendResponse.Success)
                    {
                        // Folder already existed in the filesystem, this is an update

                        // Set the parentID
                        if (incomingObj.ParentID != UUID.Zero)
                            folder.ParentID = incomingObj.ParentID;

                        // Update the version
                        ++folder.Version;
                    }
                    else
                    {
                        // No folder previously existed, this is a create
                        folder = new InventoryFolder();

                        // Set the folderID
                        folder.ID = (incomingObj.ID != UUID.Zero) ?
                            incomingObj.ID :
                            UUID.Random();

                        // Set the parentID
                        folder.ParentID = (incomingObj.ParentID != UUID.Zero) ?
                            incomingObj.ParentID :
                            server.FilesystemProvider.GetDefaultParent(message.Identity, message.AgentID, "application/vnd.ll.folder");

                        // Set the preferred content type
                        folder.Type = (short)CableBeachUtils.ContentTypeToSLAssetType(incomingObj.PreferredContentType);

                        // Set the version
                        folder.Version = 1;
                    }

                    // Set the name
                    if (!String.IsNullOrEmpty(incomingObj.Name))
                        folder.Name = incomingObj.Name;

                    if (server.FilesystemProvider.TryCreateFolder(message.Identity, message.AgentID, folder) == BackendResponse.Success)
                    {
                        reply.Success = true;
                        reply.Object = InventoryToMessage(folder);
                    }
                    else
                    {
                        Logger.Warn("[FilesystemService] Failed to create folder " + folder.Name + " for " + message.Identity);
                        reply.Success = false;
                        reply.Message = "folder creation failed";
                    }

                    #endregion InventoryFolder AddOrUpdate
                }

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void PurgeFolderHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                PurgeFolderMessage message = new PurgeFolderMessage();
                message.Deserialize(map);

                PurgeFolderReplyMessage reply = new PurgeFolderReplyMessage();

                if (server.FilesystemProvider.TryPurgeFolder(message.Identity, message.AgentID, message.FolderID) == BackendResponse.Success)
                {
                    reply.Success = true;
                }
                else
                {
                    reply.Success = false;
                    reply.Message = "failed to purge folder";
                }

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void DeleteObjectHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                DeleteObjectMessage message = new DeleteObjectMessage();
                message.Deserialize(map);

                DeleteObjectReplyMessage reply = new DeleteObjectReplyMessage();

                if (server.FilesystemProvider.TryDeleteItem(message.Identity, message.AgentID, message.ObjectID) == BackendResponse.Success ||
                    server.FilesystemProvider.TryDeleteFolder(message.Identity, message.AgentID, message.ObjectID) == BackendResponse.Success)
                {
                    reply.Success = true;
                }
                else
                {
                    reply.Success = false;
                    reply.Message = "failed to delete object";
                }

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetFolderContentsHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetFolderContentsMessage message = new GetFolderContentsMessage();
                message.Deserialize(map);

                GetFolderContentsReplyMessage reply = new GetFolderContentsReplyMessage();

                InventoryCollection contents;
                if (server.FilesystemProvider.TryFetchFolderContents(message.Identity, message.AgentID, message.FolderID, out contents) == BackendResponse.Success)
                {
                    reply.Objects = new InventoryBlock[contents.Folders.Count + contents.Items.Count];
                    int i = 0;

                    foreach (InventoryFolder invFolder in contents.Folders.Values)
                        reply.Objects[i++] = InventoryToMessage(invFolder);

                    foreach (InventoryItem invItem in contents.Items.Values)
                        reply.Objects[i++] = InventoryToMessage(invItem);
                }

                if (contents == null)
                    reply.Objects = new InventoryBlock[0];

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetFolderForTypeHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetFolderForTypeMessage message = new GetFolderForTypeMessage();
                message.Deserialize(map);

                GetFolderForTypeReplyMessage reply = new GetFolderForTypeReplyMessage();

                UUID folderID = server.FilesystemProvider.GetDefaultParent(message.Identity, message.AgentID, message.ContentType);

                if (folderID != UUID.Zero)
                {
                    InventoryFolder invFolder;
                    if (server.FilesystemProvider.TryFetchFolder(message.Identity, message.AgentID, folderID, out invFolder) == BackendResponse.Success)
                    {
                        GetFolderForTypeReplyMessage.Folder folder = new GetFolderForTypeReplyMessage.Folder();
                        folder.FolderID = invFolder.ID;
                        folder.Name = invFolder.Name;
                        folder.ParentID = invFolder.ParentID;
                        folder.PreferredContentType = CableBeachUtils.SLAssetTypeToContentType(invFolder.Type);

                        reply.FolderForType = folder;
                    }
                }

                if (reply.FolderForType == null)
                    reply.FolderForType = new GetFolderForTypeReplyMessage.Folder();

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetObjectHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetObjectMessage message = new GetObjectMessage();
                message.Deserialize(map);

                GetObjectReplyMessage reply = new GetObjectReplyMessage();

                InventoryItem item;
                InventoryFolder folder;

                if (server.FilesystemProvider.TryFetchItem(message.Identity, message.AgentID, message.ObjectID, out item) == BackendResponse.Success)
                {
                    reply.Success = true;
                    reply.Object = InventoryToMessage(item);
                }
                else if (server.FilesystemProvider.TryFetchFolder(message.Identity, message.AgentID, message.ObjectID, out folder) == BackendResponse.Success)
                {
                    reply.Success = true;
                    reply.Object = InventoryToMessage(folder);
                }
                else
                {
                    reply.Success = false;
                    reply.Message = "item or folder not found";
                }

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetFilesystemSkeletonHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetInventorySkeletonMessage message = new GetInventorySkeletonMessage();
                message.Deserialize(map);

                GetInventorySkeletonReplyMessage reply = new GetInventorySkeletonReplyMessage();

                List<InventoryFolder> folders;
                if (server.FilesystemProvider.TryFetchFolderList(message.Identity, message.AgentID, out folders) == BackendResponse.Success)
                {
                    reply.Folders = new GetInventorySkeletonReplyMessage.Folder[folders.Count];
                    for (int i = 0; i < folders.Count; i++)
                    {
                        InventoryFolder invFolder = folders[i];

                        GetInventorySkeletonReplyMessage.Folder folder = new GetInventorySkeletonReplyMessage.Folder();
                        folder.FolderID = invFolder.ID;
                        folder.Name = invFolder.Name;
                        folder.ParentID = invFolder.ParentID;
                        folder.PreferredContentType = CableBeachUtils.SLAssetTypeToContentType(invFolder.Type);

                        reply.Folders[i] = folder;
                    }
                }

                if (reply.Folders == null)
                    reply.Folders = new GetInventorySkeletonReplyMessage.Folder[0];

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetFilesystemHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetInventoryMessage message = new GetInventoryMessage();
                message.Deserialize(map);

                GetInventoryReplyMessage reply = new GetInventoryReplyMessage();

                InventoryCollection inventory;
                if (server.FilesystemProvider.TryFetchFilesystem(message.Identity, message.AgentID, out inventory) == BackendResponse.Success)
                {
                    reply.Objects = new InventoryBlock[inventory.Folders.Count + inventory.Items.Count];
                    int i = 0;

                    foreach (InventoryFolder invFolder in inventory.Folders.Values)
                        reply.Objects[i++] = InventoryToMessage(invFolder);

                    foreach (InventoryItem invItem in inventory.Items.Values)
                        reply.Objects[i++] = InventoryToMessage(invItem);
                }

                if (inventory == null)
                    reply.Objects = new InventoryBlock[0];

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetRootFolderHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetRootFolderMessage message = new GetRootFolderMessage();
                message.Deserialize(map);

                GetRootFolderReplyMessage reply = new GetRootFolderReplyMessage();
                reply.Message = String.Empty;

                InventoryFolder rootFolder;
                if (server.FilesystemProvider.TryFetchRootFolder(message.Identity, message.AgentID, out rootFolder) == BackendResponse.Success)
                {
                    reply.Success = true;
                    reply.RootFolder = (InventoryBlockFolder)InventoryToMessage(rootFolder);
                }

                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        void GetActiveGesturesHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state)
        {
            OSDMap map;
            if (ServiceHelper.TryGetOSD(request, out map))
            {
                GetActiveGesturesMessage message = new GetActiveGesturesMessage();
                message.Deserialize(map);

                GetActiveGesturesReplyMessage reply = new GetActiveGesturesReplyMessage();

                List<InventoryItem> gestures;
                if (server.FilesystemProvider.TryFetchActiveGestures(message.Identity, message.AgentID, out gestures) == BackendResponse.Success)
                {
                    reply.Gestures = new GetActiveGesturesReplyMessage.Gesture[gestures.Count];
                    for (int i = 0; i < gestures.Count; i++)
                    {
                        GetActiveGesturesReplyMessage.Gesture gesture = new GetActiveGesturesReplyMessage.Gesture();
                        gesture.AssetID = gestures[i].AssetID;
                        gesture.ItemID = gestures[i].ID;
                        reply.Gestures[i] = gesture;
                    }
                }
                else
                {
                    reply.Gestures = new GetActiveGesturesReplyMessage.Gesture[0];
                }

                Logger.Debug("[CableBeachFrontend] Responding with " + reply.Gestures.Length + " active gestures for " + message.Identity);
                ServiceHelper.SendResponse(response, reply.Serialize());
            }
        }

        #endregion Filesystem Handlers

        static InventoryBlock InventoryToMessage(InventoryBase obj)
        {
            if (obj is InventoryItem)
            {
                InventoryItem item = (InventoryItem)obj;
                InventoryBlockItem itemObj = new InventoryBlockItem();

                itemObj.AssetID = item.AssetID;
                itemObj.ContentType = CableBeachUtils.SLAssetTypeToContentType(item.AssetType);
                itemObj.CreationDate = OpenMetaverse.Utils.UnixTimeToDateTime(item.CreationDate);
                itemObj.CreatorID = item.Creator;
                itemObj.Description = item.Description;
                itemObj.Flags = item.Flags;
                itemObj.GroupID = item.GroupID;
                itemObj.GroupOwned = item.GroupOwned;
                itemObj.ID = item.ID;
                itemObj.Name = item.Name;
                itemObj.OwnerID = item.Owner;
                itemObj.ParentID = item.ParentID;
                itemObj.PermsBase = item.BasePermissions;
                itemObj.PermsEveryone = item.EveryOnePermissions;
                itemObj.PermsGroup = item.GroupPermissions;
                itemObj.PermsNext = item.NextPermissions;
                itemObj.PermsOwner = item.CurrentPermissions;
                itemObj.SalePrice = item.SalePrice;
                itemObj.SaleType = item.SaleType;

                return itemObj;
            }
            else
            {
                InventoryFolder folder = (InventoryFolder)obj;
                InventoryBlockFolder folderObj = new InventoryBlockFolder();

                folderObj.ID = folder.ID;
                folderObj.Name = folder.Name;
                folderObj.OwnerID = folder.Owner;
                folderObj.ParentID = folder.ParentID;
                folderObj.PreferredContentType = CableBeachUtils.SLAssetTypeToContentType(folder.Type);
                folderObj.Version = folder.Version;

                if (folder.Children != null)
                {
                    folderObj.Children = new InventoryBlock[folder.Children.Count];
                    int i = 0;
                    foreach (InventoryBase child in folder.Children.Values)
                        folderObj.Children[i++] = InventoryToMessage(child);
                }

                return folderObj;
            }
        }
    }
}
